#ifndef     __READER_H
#define     __READER_H

#include "../driver/cmd_uart.h"
#include "../utils/alg.h"

namespace conn {

template <uint32_t BUF_SIZE, uint32_t TIMEOUT = 100>
class ProtoReader {

public:
    utils::ByteView read() {

        uint8_t len;
        for (;;) {
            wait_data();
            
            if (!sync(len)) {
                continue;
            }

            len -= 3;
            if (!take(m_buf, len)) {
                continue;
            }

            if (m_buf[len - 2] != utils::alg::xor_sum(m_buf, len - 2)) {
                continue;
            }

            if (m_buf[len - 1] != frame::END) {
                continue;
            }

            return utils::ByteView(m_buf, len);
        }
    }

private:
    void wait_data() {
        if (m_bw.len == 0) {
            driver::cmdUart.poll(m_bw);       
        }
    }

    bool sync(uint8_t &len) {
        bool flag = false;
        uint8_t v;
        while (true) {
            if (!take(v)) {
                return false;
            }
            if (flag && (v == frame::HEAD1)) {
                break;
            }
            flag = v == frame::HEAD0;
        }
        if (!take(len)) {
            return false;
        }
        return len >= 7;
    }

    bool take(uint8_t &v) {
        if ((m_bw.len == 0) && (driver::cmdUart.poll(m_bw, TIMEOUT) != osOK)) {
            return false;
        }
        v = m_bw.buf[0];
        m_bw.buf += 1;
        m_bw.len -= 1;
        return true;
    }

    bool take(uint8_t *buf, uint8_t len) {
        uint8_t index = 0;
        while (index < len) {
            if ((m_bw.len == 0) && (driver::cmdUart.poll(m_bw, TIMEOUT) != osOK)) {
                return false;
            }
            uint8_t n = len - index;
            if (m_bw.len < n) {
                n = m_bw.len;
            }
            memcpy(buf + index, m_bw.buf, n);
            m_bw.buf += n;
            m_bw.len -= n;
            index += n;
        }
        return true;
    }

private:
    utils::ByteView m_bw;
    uint8_t m_buf[BUF_SIZE];
};

}

#endif
